// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespafeeder;

import com.yahoo.clientmetrics.MessageTypeMetricSet;
import com.yahoo.clientmetrics.RouteMetricSet;
import com.yahoo.concurrent.Timer;
import com.yahoo.metrics.Metric;
import com.yahoo.metrics.MetricSet;
import com.yahoo.metrics.MetricVisitor;
import com.yahoo.metrics.SumMetric;

import java.io.IOException;
import java.io.PrintStream;
import java.math.RoundingMode;
import java.text.NumberFormat;
import java.util.Locale;

/**
 * Class that takes progress from the feed and prints to a stream.
 */
public class ProgressPrinter implements RouteMetricSet.ProgressCallback {
    private long startTime = 0;
    private long lastProgressTime = 0;
    private long lastVerboseProgress = 0;
    final Timer timer;
    final PrintStream output;

    public ProgressPrinter(Timer timer, PrintStream output) {
        this.timer = timer;
        this.output = output;

        startTime = timer.milliTime();
        lastProgressTime = startTime;
        lastVerboseProgress = startTime;
    }

    class PrintVisitor extends MetricVisitor {
        final PrintStream out;
        final NumberFormat format;

        PrintVisitor(PrintStream out) {
            this.out = out;
            format = NumberFormat.getNumberInstance(Locale.US);
            format.setMaximumFractionDigits(2);
            format.setMinimumFractionDigits(2);
            format.setMinimumIntegerDigits(1);
            format.setParseIntegerOnly(false);
            format.setRoundingMode(RoundingMode.HALF_UP);
            format.setGroupingUsed(false);
        }

        @Override
        public boolean visitMetricSet(MetricSet set, boolean autoGenerated) {
            if (set instanceof MessageTypeMetricSet && !set.getName().equals("total")) {
                Metric m = set.getMetric("latency");
                Metric count = set.getMetric("count");
                Metric err = set.getMetric("errors.total");

                long okCount = 0, errCount = 0, ignored = 0;
                long minLatency = 0, maxLatency = 0, avgLatency = 0;

                if (m != null) {
                    minLatency = m.getLongValue("min");
                    maxLatency = m.getLongValue("max");
                    avgLatency = m.getLongValue("average");
                }
                if (count != null) {
                    okCount = count.getLongValue("count");
                }
                Metric ignoredMetric = set.getMetric("ignored");
                if (ignoredMetric != null) {
                    ignored = ignoredMetric.getLongValue("count");
                }

                if (err != null) {
                    errCount = err.getLongValue("count");
                }

                long timeSinceStart = timer.milliTime() - startTime;

                out.println(((MessageTypeMetricSet)set).getMessageName() + ":\t" +
                        "ok: " + okCount +
                        " msgs/sec: " + format.format((double)okCount * 1000 / timeSinceStart) +
                        " failed: " + errCount +
                        " ignored: " + ignored +
                        " latency(min, max, avg): " + minLatency + ", " + maxLatency + ", " + avgLatency);
            }
            return true;
        }
    }

    public static String getDashes(int count) {
        String dashes = "";
        for (int i = 0; i < count; i++) {
            dashes += "-";
        }

        return dashes;
    }

    public synchronized void renderStatusText(RouteMetricSet metrics, PrintStream stream) throws IOException {
        String headline = "Messages sent to vespa (route " + metrics.getName() + ") :";
        stream.println(headline);
        stream.println(getDashes(headline.length()));
        metrics.visit(new PrintVisitor(stream), false);
    }

    public long getOkMessageCount(RouteMetricSet metrics) {
        SumMetric sum = (SumMetric)metrics.getMetric("total");

        MetricSet ms = (MetricSet)sum.generateSum();
        if (ms != null) {
            Metric latency = ms.getMetric("latency");
            if (latency != null) {
                return latency.getLongValue("count");
            }
        }

        return 0;
    }

    @Override
    public void onProgress(RouteMetricSet metrics) {
        try {
            long timeNow = timer.milliTime();

            if (timeNow - lastVerboseProgress > 30000) {
                output.println("\n");
                renderStatusText(metrics, output);
                lastVerboseProgress = timeNow;
            } else if (timeNow - lastProgressTime > 1000) {
                output.print("\rSuccessfully sent " + getOkMessageCount(metrics) + " messages so far");
                lastProgressTime = timeNow;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void done(RouteMetricSet metrics) {
        try {
            output.println("\n");
            renderStatusText(metrics, output);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
